package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.jugile.util.IDO;

/**
 * "For the invisible things of him from the creation of the world are clearly seen, 
 *  being understood by the things that are made, even his eternal power and Godhead; 
 *  so that they are without excuse" (Romans 1:20)
 * 
 * ==========
 * 
 * Bos = Business Object Set for managing sets of business domain objects.
 * @author jukka.rahkonen@iki.fi
 */
public class Bos<E extends IDO> extends Jugile implements Iterable<E> {

	private HashMap<Long,E> items = null;
	
	public Bos<E> add(E item) { if (item != null) items.put(item.id(), item); return this; }
	public Bos<E> add(Bos<E> list) { items.putAll(list.items); return this; }
	public Bos<E> add(List<E> list) { for (E o : list) items.put(o.id(), o); return this; }
	
	private Bos(HashMap<Long,E> m) { items = m; }
	public Bos() { items = new HashMap<Long,E>(); }	
	public Bos<E> clone() { return new Bos<E>((HashMap<Long,E>)items.clone()); }

	public Iterator<E> iterator() { return items.values().iterator(); }
	public void remove(E item) { if (item != null) items.remove(item.id());	}
	public void remove(long id) { items.remove(id); }
	public int size() { return items.size(); }
	public E first() { return iterator().next(); }
	public boolean contains(E item) { return item == null? null : items.containsKey(item.id()); }
	public Set<Long> getKeys() { return items.keySet(); }
	
	public E get(long id) { return items.get(id); }
	
	// ----- logical operators -----
	
	public Bos<E> not(Bos<E> bos) { for (E i : bos) remove(i); return this;}	
	public Bos<E> or(Bos<E> bos) { for (E i : bos) add(i); return this; }
	public Bos<E> and(Bos<E> bos) {
		HashMap<Long,E> res = new HashMap<Long,E>();
		for (E i : this) if (bos.contains(i)) res.put(i.id(),i);
		items = res;
		return this;
	}
	public Bos<E> xor(Bos<E> bos) {
		for (E i : bos) {
			if (contains(i)) remove(i);
			else add(i);
		}
		return this;
	}	

	// ----- filters -----
	public Bos<E> keep(String mname) { 
		HashMap<Long,E> res = new HashMap<Long,E>();
		for (E i : this) if (isTrue(i,mname)) res.put(i.id(),i);
		items = res;
		return this;		
	}
	public Bos<E> not(String mname) { 
		HashMap<Long,E> res = new HashMap<Long,E>();
		for (E i : this) if (!isTrue(i,mname)) res.put(i.id(),i);
		items = res;
		return this;
	}

	public Bos<E> matches(Object o) {
		Bos<E> res = new Bos<E>();
		for (E i : this) if (i.matches(o)) res.add(i);
		return res;
	}

	public List<E> page(int page, int size, String sortcrit) {
		return (List<E>)page((List)sort(sortcrit), page, size);
	}
	
	public List<E> sublist(int start, int end, String sortcrit) {
		return (List<E>)sublist((List)sort(sortcrit), start, end);
	}
	
	private boolean isTrue(Object item, String mname) {
		Proxy p = new Proxy(item);
		return isTrue(p.call(mname));
	}
	private boolean isTrue(Object res) {
		if (res instanceof Boolean) {
			if ((Boolean)res) return true;
			return false;
		}
		return res != null;
	}

	public Bos<E> query(Query q) { return query(q,false); }
	public Bos<E> queryNot(Query q) { return query(q,true); }
	public Bos<E> query(Query q, boolean not) {
		Bos<E> res = new Bos<E>();
		for (E i : this) {
			if (test(q,i)) {
				if (!not) res.items.put(i.id(), i);
			} else {
				if (not) res.items.put(i.id(), i);
			}
		}
		return res;
	}

	private boolean test(Query q, Object item) {
		return q.eval(item);
	}
	
	// ----- sorting -------
	
	public List<E> sort(String crit) {
		List<E> res = new ArrayList<E>(items.values());
		Collections.sort(res, new IDOComparator(crit));
		return res;
	}
	
	// -------- sort util --------
	
	public static void sort(List list, String crit) {
		Collections.sort(list, new IDOComparator(crit));
	}
	
	// -------- paging util --------

	/**
	 * Paging starts from 0.
	 */
	public static List page(List list, int page, int size) {
		int start = page*size;
		int end = start + size;
		if (start >= list.size()) start = list.size();
		if (start < 0) start = 0;
		if (end >= list.size()) end = list.size();
		if (end < 0) end = 0;
		return list.subList(start, end);
	}

	public static List sublist(List list, int start, int end) {
		return list.subList(start, end);
	}
	
}
